package channel;

import colour.ColourUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.URL;
import java.util.ArrayList;

import console.IOBuffer;
import server.util.ServerConstants;

public class ChannelImp extends Thread implements Channel {

    private String channelName;
    private double version;
    private ServerSocket ss;
    private int port = 724;
    private ChannelServer listener;
    private ArrayList<ChannelItem> sockets;
    private boolean running;
    private IOBuffer terminal;
    private String motd;
    private ChannelMasterComms master;
    private String globalip;

    public ChannelImp(String name, IOBuffer t) {
        terminal = t;
        master = new ChannelMasterComms();
        channelName = name;
        version = 0.8;
        sockets = new ArrayList<ChannelItem>();
        motd = "";
    }

    @Override
    public void run() {
        running = true;
        try {
            terminal.writeTo("\nStarting channel...\n");
            ss = new ServerSocket(port);

            // Listen for connections
            listener = new ChannelServer(ss, sockets, motd);
            listener.start();

            // Print readout
            terminal.writeTo(ColourUtil.colourise("\nWITNA Channel v" + version, ColourUtil.magenta));
            terminal.writeTo(ColourUtil.colourise("\n-----", ColourUtil.white));
            terminal.writeTo(ColourUtil.colourise("\n-", ColourUtil.white)
                    + ColourUtil.colourise("Channel     : ", ColourUtil.magenta)
                    + ColourUtil.colourise(channelName, ColourUtil.yellow));
            terminal.writeTo(ColourUtil.colourise("\n-", ColourUtil.white)
                    + ColourUtil.colourise("Global ip   : ", ColourUtil.magenta));

            final int serverPort = port;
            Runnable task = new Runnable() {

                @Override
                public void run() {
                    // Try and get internet IP
                    String globalip = getGlobalIP();

                    // Try and get network IP
                    String localip = getLocalIP();

                    // Finish output
                    terminal.writeTo(ColourUtil.colourise(globalip + ":" + serverPort, ColourUtil.yellow));
                    terminal.writeTo(ColourUtil.colourise("\n-", ColourUtil.white)
                            + ColourUtil.colourise("Local  ip   : ", ColourUtil.magenta)
                            + ColourUtil.colourise(localip + ":" + serverPort, ColourUtil.yellow));
                    String status = getNetStatus(globalip, localip);
                    terminal.writeTo(ColourUtil.colourise("\n-", ColourUtil.white)
                            + ColourUtil.colourise("Connection  : ", ColourUtil.magenta)
                            + ColourUtil.colourise(status, ColourUtil.yellow)
                            + ColourUtil.colourise("\n", ColourUtil.grey));
                }
            };

            new Thread(task).start();

            // Remove inactive items
            while (running) {
                // Sleep and remove idle sockets
                Thread.sleep(1000);
                for (int x = 0; x < sockets.size(); x++) {
                    if (!sockets.get(x).isConnected()) {
                        sockets.remove(x);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // For the local client users commands
    @Override
    public void processLocalCommand(String input) {
        if (input.equalsIgnoreCase("DISCONNECT") || input.equalsIgnoreCase("QUIT")) {
            quit();
        } else if (input.toUpperCase().startsWith("MOTD")) {
            if (input.length() == 4) {
                terminal.writeTo("\nSet the Message of the Day to what?");
            } else if (input.length() == 5 && input.charAt(4) == ' ') {
                terminal.writeTo("\nSet the Message of the Day to what?");
            } else if (input.length() > 5 && input.charAt(4) == ' ') {
                setMOTD(input.substring(5));
            }
        } else if (input.toUpperCase().startsWith("MASTERCHANNELJOIN")) {
            if (input.length() == 17) {
                terminal.writeTo("\nChannel not specified.\nSyntax: masterchanneljoin <channelipaddress>");
            } else if (input.length() == 18 && input.charAt(17) == ' ') {
                terminal.writeTo("\nChannel not specified.\nSyntax: masterchanneljoin <channelipaddress>");
            } else if (input.length() > 18 && input.charAt(17) == ' ') {
                String channelIP = input.substring(18).trim();
                setMasterChannel(channelIP);
            }
        } else {
            terminal.writeTo("\nChannel command not recognised.");
        }
    }

    private void setMOTD(String motd) {
        this.motd = motd;
        master.setMOTD(motd);

        terminal.writeTo("\nMessage of the Day set to: " + motd);
    }

    private void setMasterChannel(String channelIP) {
        final String chanDets = "`" + channelName + "` " + "`" + motd + "` "
                + "`" + sockets.size() + "`";
        if (channelIP.equalsIgnoreCase("DEFAULT")) {
            terminal.writeTo("\nFetching master channel IP..");
            channelIP = GrabChannelData.getDefaultChannelIP();
        }
        final String finalIP = channelIP;
        terminal.writeTo("\nCommunicating with channel..");

        Thread task = new Thread() {

            @Override
            public void run() {
                terminal.writeTo(master.connectToMaster(finalIP, chanDets, sockets));
                if (master.isCommunicating()) {
                    master.start();
                }
            }
        };
        task.start();
    }

    public void quit() {
        try {
            ss.close();
            ss = null;
            master.stopCommunicating();

            listener.interrupt();
            listener.quit();
            running = false;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private String getGlobalIP() {
        try {
            URL whatismyip = new URL(ServerConstants.externalIPAddr);
            InputStream stream = whatismyip.openStream();
            BufferedReader in = new BufferedReader(new InputStreamReader(stream));

            String ip = in.readLine();

            stream.close();
            in.close();

            if (ip != null) {
                return ip;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "127.0.0.1";
    }

    private String getLocalIP() {
        try {
            InetAddress addr = InetAddress.getLocalHost();
            return addr.getHostAddress();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "127.0.0.1";
    }

    private String getNetStatus(String globalip, String localip) {
        if (!globalip.equals("127.0.0.1")) {
            return "ONLINE";
        } else {
            if (!localip.equals("127.0.0.1")) {
                return "NETWORK";
            } else {
                return "OFFLINE";
            }
        }
    }
}
